<!DOCTYPE html>
<!-- saved from url=(0043)http://keith-wood.name/pluginFramework.html -->
<html class="js flexbox canvas canvastext webgl no-touch geolocation postmessage websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients cssreflections csstransforms no-csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers applicationcache svg inlinesvg smil svgclippaths" style=""><!--<![endif]--><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link href="http://keith-wood.name/kw-name.rss.xml" type="application/rss+xml" rel="alternate" title="RSS Feed">
<meta charset="utf-8">
<title>A jQuery Plugin Framework</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<link rel="icon" type="image/ico" href="http://keith-wood.name/favicon.ico">
<link rel="stylesheet" href="http://keith-wood.name/css/normalize.css">
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.23/themes/south-street/jquery-ui.css">
<link rel="stylesheet" href="http://keith-wood.name/css/jquery.bookmark.css">
<link rel="stylesheet" href="http://keith-wood.name/css/demo.css">
<style>
pre { padding: 0.5em; }
#index { position: fixed; right: 0em; top: 3em; padding: 0em; list-style: none; font-size: 90%; text-align: right; }
#index li { margin: 0.25em 0em; padding: 0em 0.5em; }
#index #popup { margin: 0em; padding: 0.25em 0.5em; text-align: center; }
</style>
<script src="./A jQuery Plugin Framework_files/modernizr-2.6.2.min.js"></script>
<script src="./A jQuery Plugin Framework_files/jquery.min.js"></script>
<script src="./A jQuery Plugin Framework_files/jquery-ui.min.js"></script>
<script src="./A jQuery Plugin Framework_files/jquery.bookmark.js"></script>
<script src="./A jQuery Plugin Framework_files/jquery.chili-2.2.js"></script>
<script src="./A jQuery Plugin Framework_files/demo.js"></script>
<script>
$(function() {
	$('#popup').click(function() { $('#index li').toggle(); });
	$('#index li:not(:first)').click(function() { $('#popup').click(); }).hide();
	$(document).click(function(event) {
		if ($(event.target).closest('#index').length == 0 &&
				!$('#popup').is(':visible')) {
			$('#popup').click();
		}
	});
});
</script>
<style type="text/css">/* Chili -- js */
.js__ml_comment { color: green; }
.js__sl_comment { color: green; }
.js__string { color: blue; }
.js__num { color: red; }
.js__reg_exp { color: maroon; }
.js__brace { font-weight: bold; }
.js__statement { color: black; font-weight: bold; }
.js__error { color: coral; }
.js__object { color: purple; font-weight: bold; }
.js__property { color: purple; font-weight: bold; }
.js__function { color: olive; }
.js__operator { color: navy; font-weight: bold; }
.js__liveconnect { text-decoration: overline; }</style><style type="text/css">/* Chili -- html */
.html__doctype { color: purple; }
.html__ie_style { color: DarkSlateGray; font-weight: bold; }
.html__comment { color: green; }
.html__tag_start { color: navy; font-weight: bold; }
.html__tag_end { color: navy; font-weight: bold; }
.html__entity { color: green; }
.html__attr_name { color: purple; }
.html__attr_value { color: blue; }</style><link type="text/css" rel="stylesheet" charset="UTF-8" href="./A jQuery Plugin Framework_files/translateelement.css"><script type="text/javascript" charset="UTF-8" src="./A jQuery Plugin Framework_files/main_pt-BR.js"></script><script type="text/javascript" charset="UTF-8" src="./A jQuery Plugin Framework_files/element_main.js"></script></head>
<body style="">
<ul id="index" class="ui-state-active">
	<li id="popup">I<br>N<br>D<br>E<br>X</li>
	<li style="display: none;"><a href="http://keith-wood.name/pluginFramework.html#intro">Introduction</a></li>
	<li style="display: none;"><a href="http://keith-wood.name/pluginFramework.html#case">Case Study</a></li>
	<li style="display: none;"><a href="http://keith-wood.name/pluginFramework.html#invoke">First Steps</a></li>
	<li style="display: none;"><a href="http://keith-wood.name/pluginFramework.html#manager">Singleton Manager</a></li>
	<li style="display: none;"><a href="http://keith-wood.name/pluginFramework.html#attach">Attachment</a></li>
	<li style="display: none;"><a href="http://keith-wood.name/pluginFramework.html#change">Change Settings</a></li>
	<li style="display: none;"><a href="http://keith-wood.name/pluginFramework.html#body">Body</a></li>
	<li style="display: none;"><a href="http://keith-wood.name/pluginFramework.html#length">Current Length</a></li>
	<li style="display: none;"><a href="http://keith-wood.name/pluginFramework.html#destroy">Destruction</a></li>
	<li style="display: none;"><a href="http://keith-wood.name/pluginFramework.html#test">Testing</a></li>
	<li style="display: none;"><a href="http://keith-wood.name/pluginFramework.html#publish">Publishing</a></li>
	<li style="display: none;"><a href="http://keith-wood.name/pluginFramework.html#download">Downloads</a></li>
</ul>
<header role="banner">
	<div id="header-links"><a id="home" href="http://keith-wood.name/index.html"><img src="./A jQuery Plugin Framework_files/homePage.png" alt="Home page" title="Home page"></a>&nbsp;<a href="http://keith-wood.name/pluginFramework.html#" id="donate"><img src="./A jQuery Plugin Framework_files/donate.png" alt="Donate" title="Donate"></a>&nbsp;<div id="donations" style=""><h3>Donations</h3><div>If you would like to donate something for my efforts, please visit my <a href="http://www.amazon.com/wishlist/2OEDAIL14M64W/ref=cm_wl_rlist_go" target="_blank">Amazon Wishlist</a> or donate directly through <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank"><input type="hidden" name="cmd" value="_s-xclick"><input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----MIIHNwYJKoZIhvcNAQcEoIIHKDCCByQCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYB33bliXOtFtvHh2ny/3KxX525RgKcAR9XSKCxL2vLIvXQmNL0NH6MQwrRHXhQR0SFXqatxVKoViGDY5HvjJxOnxJ/Kyn+DrU6EGaYZaKZpbOh5LVjWFKZVcSGS2WgLUFDOsR/46a+0LK9iWkkgRXXapvpmGgVnMpNRAPU7eUHCgDELMAkGBSsOAwIaBQAwgbQGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQIcrnteGhsO/KAgZCvegEQgxDUrDAv5sAI24qfPg45zl7W1lb8UssjhdS5uy63hHXJDVYZZ2bgjVqCM3niy9GIQ8Pdv5/7X34bLrViuNmQvMmWpq/C9cScnDmNoeAwxKIHYcaY5QgjdqkRaWP7edDtBxKjLGJaAPSsbCeGMnBMjLpCQJQG/NX+saXDpByUJpPydyNEz90L9MMrUrigggOHMIIDgzCCAuygAwIBAgIBADANBgkqhkiG9w0BAQUFADCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20wHhcNMDQwMjEzMTAxMzE1WhcNMzUwMjEzMTAxMzE1WjCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMFHTt38RMxLXJyO2SmS+Ndl72T7oKJ4u4uw+6awntALWh03PewmIJuzbALScsTS4sZoS1fKciBGoh11gIfHzylvkdNe/hJl66/RGqrj5rFb08sAABNTzDTiqqNpJeBsYs/c2aiGozptX2RlnBktH+SUNpAajW724Nv2Wvhif6sFAgMBAAGjge4wgeswHQYDVR0OBBYEFJaffLvGbxe9WT9S1wob7BDWZJRrMIG7BgNVHSMEgbMwgbCAFJaffLvGbxe9WT9S1wob7BDWZJRroYGUpIGRMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbYIBADAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAIFfOlaagFrl71+jq6OKidbWFSE+Q4FqROvdgIONth+8kSK//Y/4ihuE4Ymvzn5ceE3S/iBSQQMjyvb+s2TWbQYDwcp129OPIbD9epdr4tJOUNiSojw7BHwYRiPh58S1xGlFgHFXwrEBb3dgNbMUa+u4qectsMAXpVHnD9wIyfmHMYIBmjCCAZYCAQEwgZQwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tAgEAMAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xMzAzMjUwNTA2MThaMCMGCSqGSIb3DQEJBDEWBBSPoRvOzgEsqgKwpH1bwzMV32tFMjANBgkqhkiG9w0BAQEFAASBgD7gFfun3/yYHEysaOyjayxZdrs3zX6iIp87YC7h/TPvinhwStRV7G77cikKsDYNB4Gkc4irHubN/46oDrJizquyAME5wrl+mOeicjsDzGRd1iaJVymf+HliBguGcItVQkSdkK7W9RveeSb/bX7U/vl7Lgn8PlUOSjSoFdyfQfNh-----END PKCS7-----"><input type="image" src="./A jQuery Plugin Framework_files/paypal.png" border="0" name="submit" alt="PayPal - The safer, easier way to pay online."><img alt="" border="0" src="./A jQuery Plugin Framework_files/pixel.gif" width="1" height="1"></form>.</div><p>Thank you to those that have already donated - I'm enjoying the music and dancing!</p></div><a href="http://keith-wood.name/pluginFramework.html#" id="crosslink"><img src="./A jQuery Plugin Framework_files/plugins.png" alt="Other plugins" title="Other plugins"></a>&nbsp;<div id="crosslinks" style=""><ul><li><a href="http://keith-wood.name/backgroundPos.html">Background Position</a></li><li><a href="http://keith-wood.name/bookmark.html">Bookmark</a></li><li><a href="http://keith-wood.name/calculator.html">Calculator</a></li><li><a href="http://keith-wood.name/calendars.html">Calendars</a></li><li><a href="http://keith-wood.name/countdown.html">Countdown</a></li><li><a href="http://keith-wood.name/dateEntry.html">Date Entry</a></li><li><a href="http://keith-wood.name/datetimeEntry.html">Date/Time Entry</a></li><li><a href="http://keith-wood.name/datepick.html">Datepicker</a></li><li><a href="http://keith-wood.name/flightBoard.html">Flight Board</a></li><li><a href="http://keith-wood.name/gChart.html">Google Chart</a></li><li><a href="http://keith-wood.name/gsblogbar.html">Google Search Blogbar</a></li><li><a href="http://keith-wood.name/gsbookbar.html">Google Search Bookbar</a></li><li><a href="http://keith-wood.name/gsnewsbar.html">Google Search Newsbar</a></li><li><a href="http://keith-wood.name/gsvideobar.html">Google Search Videobar</a></li></ul><ul><li><a href="http://keith-wood.name/icalendar.html">iCalendar</a></li><li><a href="http://keith-wood.name/imageCube.html">Image Cube</a></li><li><a href="http://keith-wood.name/keypad.html">Keypad</a></li><li><a href="http://keith-wood.name/labelEffect.html">Label Effect</a></li><li><a href="http://keith-wood.name/linkedSliders.html">Linked Sliders</a></li><li><a href="http://keith-wood.name/localisation.html">Localisation</a></li><li><a href="http://keith-wood.name/maxlength.html">Max Length</a></li><li><a href="http://keith-wood.name/more.html">More</a></li><li><a href="http://keith-wood.name/realPerson.html">Real Person</a></li><li><a href="http://keith-wood.name/relationships.html">Relationships</a></li><li><a href="http://keith-wood.name/signature.html">Signature</a></li><li><a href="http://keith-wood.name/svg.html">SVG Integration</a></li><li><a href="http://keith-wood.name/themes.html">Themes</a></li><li><a href="http://keith-wood.name/timeEntry.html">Time Entry</a></li></ul></div><a href="http://keith-wood.name/kw-name.rss.xml"><img src="./A jQuery Plugin Framework_files/rss.png" alt="RSS Feed" title="RSS Feed"></a></div><h1>A jQuery Plugin Framework</h1>
</header>
<section>
<p><a href="http://jquery.com/">jQuery</a> is a JavaScript Library that simplifies
	HTML page development. As well as all the built-in functionality, jQuery is designed
	to be extensible, allowing us to enhance jQuery's abilities however we want.
	We can provide additional:</p>
<ul>
	<li>standalone functions and objects ($.xxx);</li>
	<li>jQuery selection functions ($.fn.xxx);</li>
	<li>selectors ($.expr.filters.xxx);</li>
	<li>animations ($.fx.step.xxx).</li>
</ul>
<p>It is the second category that is examined here. Most jQuery plugins fall under
	this heading, allowing us to perform an operation on a set of elements resulting from
	a jQuery selection. Although there are many third-party plugins available,
	sometimes they don't do just what we want. We can then either update an
	existing plugin or write our own from scratch.</p>
<p>This article describes a framework that I have used in many of
	<a href="http://keith-wood.name/index.html#jquery">my plugins</a>.</p>

<article>
<header><a name="case"></a>Case Study</header>
<p>To make things less esoteric, we'll go through the process of creating an
	actual plugin to show how the framework is applied.</p>
<p>HTML input fields have a <code>maxlength</code> attribute to restrict their size,
	but textareas do not. We will create a plugin to add this functionality.
	Our final code will look something like this:</p>
<pre class="ui-state-active"><code class="js">$(selector).maxlength(<span class="js__brace">{</span>max:&nbsp;<span class="js__num">300</span><span class="js__brace">}</span>);</code></pre>
<p>We want to be able to set the maximum length allowed and to provide feedback
	to the user by showing how many characters are left.
	For this we need to add event handlers to watch keystrokes
	and to add content to the page following the textarea.
	We also need to allow settings to be updated on-the-fly,
	and to be able to remove the functionality altogether.
	And, finally, we'll add a way to retrieve the current settings.</p>
<p>As we're developing this plugin, we want to keep in mind the following principles:</p>
<ul>
	<li>Don't rely on <code>$</code> being the same as <code>jQuery</code>.</li>
	<li>Hide the implementation details.</li>
	<li>Place everything under the jQuery object.</li>
	<li>Only claim a single name and use that for everything.</li>
	<li>Return the jQuery object for chaining whenever possible.</li>
	<li>Use <code>$.data</code> to store instance details.</li>
	<li>Pass commands for additional functionality, e.g. <code>$(selector).xxx('destroy')</code>.</li>
	<li>Anticipate customisations.</li>
	<li>Use sensible defaults.</li>
	<li>Allow for localisation/localization.</li>
	<li>Test on the major browsers: IE, FireFox, Safari, Opera, Chrome.</li>
	<li>Provide demonstrations and documentation.</li>
</ul>
</article>

<article>
<header><a name="invoke"></a>First Steps</header>
<p>The preferred file name for a plugin is of the form <code>jquery.xxx.js</code>.
	We should pick a name that reflects the purpose of the plugin and that we can use
	throughout, in keeping with the principle of <em>only claiming one name within the jQuery
	namespace</em>. We'll use 'maxlength' for this example. By only using a single name we
	reduce the clutter within jQuery and also the possibility of conflicting with
	another plugin or future enhancements to jQuery itself.</p>
<p>Surround the plugin code with the following to ensure that <em><code>$</code>
	is the same as <code>jQuery</code></em> and to <em>hide the implementation details</em>:</p>
<pre class="ui-state-active"><code class="js">(<span class="js__operator">function</span>($)&nbsp;<span class="js__brace">{</span>&nbsp;<span class="js__sl_comment">//&nbsp;Hide&nbsp;scope,&nbsp;no&nbsp;$&nbsp;conflict</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__sl_comment">//&nbsp;All&nbsp;the&nbsp;other&nbsp;code&nbsp;...</span>&nbsp;<br><span class="js__brace">}</span>)(jQuery);</code></pre>
<p>What this construct is doing is defining an inline, anonymous function
	and then immediately calling it with a parameter of <code>jQuery</code>.
	Since the parameter name in the function declaration is <code>$</code>
	we can safely use <code>$</code> within the body of the function
	knowing that it refers to the jQuery object, as specified by the parameter,
	regardless of whatever else might be on the page.</p>
<p>Furthermore, this function hides whatever it contains from the outside world,
	except where we want to expose particular functionalty, such as adding
	our plugin to jQuery as shown below. Thus we can declare constants and
	local variables within this function that won't interfere with or
	be affected by anything else on the page.</p>
<pre class="ui-state-active"><code class="js"><span class="js__sl_comment">//&nbsp;The&nbsp;list&nbsp;of&nbsp;commands&nbsp;that&nbsp;return&nbsp;values&nbsp;and&nbsp;don't&nbsp;permit&nbsp;chaining</span>&nbsp;<br><span class="js__statement">var</span>&nbsp;getters&nbsp;=&nbsp;[<span class="js__string">'settings'</span>];&nbsp;<br>&nbsp;<br><span class="js__ml_comment">/*&nbsp;Determine&nbsp;whether&nbsp;a&nbsp;method&nbsp;is&nbsp;a&nbsp;getter&nbsp;and&nbsp;doesn't&nbsp;permit&nbsp;chaining.&nbsp;<br>&nbsp;&nbsp;&nbsp;@param&nbsp;&nbsp;method&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(string,&nbsp;optional)&nbsp;the&nbsp;method&nbsp;to&nbsp;run&nbsp;<br>&nbsp;&nbsp;&nbsp;@param&nbsp;&nbsp;otherArgs&nbsp;&nbsp;([],&nbsp;optional)&nbsp;any&nbsp;other&nbsp;arguments&nbsp;for&nbsp;the&nbsp;method&nbsp;<br>&nbsp;&nbsp;&nbsp;@return&nbsp;&nbsp;true&nbsp;if&nbsp;the&nbsp;method&nbsp;is&nbsp;a&nbsp;getter,&nbsp;false&nbsp;if&nbsp;not&nbsp;*/</span>&nbsp;<br><span class="js__operator">function</span>&nbsp;isNotChained(method,&nbsp;otherArgs)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(method&nbsp;==&nbsp;<span class="js__string">'option'</span>&nbsp;&amp;&amp;&nbsp;(otherArgs.length&nbsp;==&nbsp;<span class="js__num">0</span>&nbsp;||&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(otherArgs.length&nbsp;==&nbsp;<span class="js__num">1</span>&nbsp;&amp;&amp;&nbsp;<span class="js__operator">typeof</span>&nbsp;otherArgs[<span class="js__num">0</span>]&nbsp;==&nbsp;<span class="js__string">'string'</span>)))&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">return</span>&nbsp;<span class="js__property">true</span>;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">return</span>&nbsp;$.inArray(method,&nbsp;getters)&nbsp;&gt;&nbsp;-<span class="js__num">1</span>;&nbsp;<br><span class="js__brace">}</span>&nbsp;<br>&nbsp;<br><span class="js__ml_comment">/*&nbsp;Attach&nbsp;the&nbsp;max&nbsp;length&nbsp;functionality&nbsp;to&nbsp;a&nbsp;jQuery&nbsp;selection.&nbsp;<br>&nbsp;&nbsp;&nbsp;@param&nbsp;&nbsp;options&nbsp;&nbsp;(object)&nbsp;the&nbsp;new&nbsp;settings&nbsp;to&nbsp;use&nbsp;for&nbsp;these&nbsp;instances&nbsp;(optional)&nbsp;or&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(string)&nbsp;the&nbsp;method&nbsp;to&nbsp;run&nbsp;(optional)&nbsp;<br>&nbsp;&nbsp;&nbsp;@return&nbsp;&nbsp;(jQuery)&nbsp;for&nbsp;chaining&nbsp;further&nbsp;calls&nbsp;or&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(any)&nbsp;getter&nbsp;value&nbsp;*/</span>&nbsp;<br>$.fn.maxlength&nbsp;=&nbsp;<span class="js__operator">function</span>(options)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">var</span>&nbsp;otherArgs&nbsp;=&nbsp;<span class="js__object">Array</span>.prototype.slice.call(<span class="js__property">arguments</span>,&nbsp;<span class="js__num">1</span>);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(isNotChained(options,&nbsp;otherArgs))&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">return</span>&nbsp;plugin[<span class="js__string">'_'</span>&nbsp;+&nbsp;options&nbsp;+&nbsp;<span class="js__string">'Plugin'</span>].apply(plugin,&nbsp;[<span class="js__operator">this</span>[<span class="js__num">0</span>]].concat(otherArgs));&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">return</span>&nbsp;<span class="js__operator">this</span>.each(<span class="js__operator">function</span>()&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(<span class="js__operator">typeof</span>&nbsp;options&nbsp;==&nbsp;<span class="js__string">'string'</span>)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(!plugin[<span class="js__string">'_'</span>&nbsp;+&nbsp;options&nbsp;+&nbsp;<span class="js__string">'Plugin'</span>])&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">throw</span>&nbsp;<span class="js__string">'Unknown&nbsp;method:&nbsp;'</span>&nbsp;+&nbsp;options;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;plugin[<span class="js__string">'_'</span>&nbsp;+&nbsp;options&nbsp;+&nbsp;<span class="js__string">'Plugin'</span>].apply(plugin,&nbsp;[<span class="js__operator">this</span>].concat(otherArgs));&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">else</span>&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;plugin._attachPlugin(<span class="js__operator">this</span>,&nbsp;options&nbsp;||&nbsp;<span class="js__brace">{</span><span class="js__brace">}</span>);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>);&nbsp;<br><span class="js__brace">}</span>;</code></pre>
<p>Here we are defining our plugin function, <code>maxlength</code>,
	as an extension to <code>$.fn</code>, the set of selection functions.
	This is the single name that we are claiming within jQuery.
	Generally, we apply the requested functionality to each of the elements
	in the current selection and return the result of the <code>each</code>
	call as the result of our new function, thus fulfilling the principle
	of <em>allowing jQuery calls to be chained</em> whenever possible.</p>
<p>Our function takes at least one parameter: <code>options</code>.
	When initially attaching the functionality this parameter is
	an object containing settings or is not present at all. In all other
	cases it will be a string value identifying a command to execute
	and may be followed by additional parameters for that command.</p>
<p>Firstly we check to see whether a command is specified that returns a value
	that prevents us chaining additional jQuery function calls (<code>isNotChained</code>).
	In most plugins, the '<code>option</code>' command may return the current set of
	options used by the plugin. Otherwise, check the list of known getter commands.
	If this is such a command, we construct the name of the target method based on the
	command and return the value of calling it, passing a reference to the first of the
	selected elements along with the remainder of the arguments as parameters.</p>
<p>Otherwise, based on whether we are initialising the plugin or processing a command,
	we either call <code>_attachPlugin</code> directly or construct the name of the
	target method based on the command (<code>_xxxPlugin</code>) and call that.
	In the latter case we also pass along any other parameters
	that may have been passed in to the original call, using the Array
	<code>slice</code> function to extract those after the command and the
	<code>apply</code> call to pass them on. For both variations we also
	pass along a reference to the current element as the first parameter.
	The <code>option || {}</code> construct ensures that a <code>null</code>
	options object is not passed along to the rest of the code.</p>
</article>

<article>
<header><a name="manager"></a>Singleton Manager</header>
<p>Having a single object to manage the interactions for the plugin allows us to centralise
	the processing. We are again using our claimed name for this object and are creating it
	within the jQuery object itself. We could create the manager outside of jQuery, but that
	would clutter the global namespace and introduce more opportunities for conflicts with
	other JavaScript functionality. In the <a href="http://keith-wood.name/pluginFramework.html#invoke">invocation</a> code we made use
	of this object when redirecting incoming calls and provided the context by passing along
	the target element as a parameter. The object can also serve as a repository for global
	settings that apply to all instances of the plugin in use (<code>_defaults</code>).</p>
<pre class="ui-state-active"><code class="js"><span class="js__ml_comment">/*&nbsp;Max&nbsp;length&nbsp;manager.&nbsp;*/</span>&nbsp;<br><span class="js__operator">function</span>&nbsp;MaxLength()&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__operator">this</span>.regional&nbsp;=&nbsp;[];&nbsp;<span class="js__sl_comment">//&nbsp;Available&nbsp;regional&nbsp;settings,&nbsp;indexed&nbsp;by&nbsp;language&nbsp;code</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__operator">this</span>.regional[<span class="js__string">''</span>]&nbsp;=&nbsp;<span class="js__brace">{</span>&nbsp;<span class="js__sl_comment">//&nbsp;Default&nbsp;regional&nbsp;settings</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;feedbackText:&nbsp;<span class="js__string">'{r}&nbsp;characters&nbsp;remaining&nbsp;({m}&nbsp;maximum)'</span>,&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__sl_comment">//&nbsp;Display&nbsp;text&nbsp;for&nbsp;feedback&nbsp;message,&nbsp;use&nbsp;{r}&nbsp;for&nbsp;remaining&nbsp;characters,</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__sl_comment">//&nbsp;{c}&nbsp;for&nbsp;characters&nbsp;entered,&nbsp;{m}&nbsp;for&nbsp;maximum</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;overflowText:&nbsp;<span class="js__string">'{o}&nbsp;characters&nbsp;too&nbsp;many&nbsp;({m}&nbsp;maximum)'</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__sl_comment">//&nbsp;Display&nbsp;text&nbsp;when&nbsp;past&nbsp;maximum,&nbsp;use&nbsp;substitutions&nbsp;above</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__sl_comment">//&nbsp;and&nbsp;{o}&nbsp;for&nbsp;characters&nbsp;past&nbsp;maximum</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__operator">this</span>._defaults&nbsp;=&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;max:&nbsp;<span class="js__num">200</span>,&nbsp;<span class="js__sl_comment">//&nbsp;Maximum&nbsp;length</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;truncate:&nbsp;<span class="js__property">true</span>,&nbsp;<span class="js__sl_comment">//&nbsp;True&nbsp;to&nbsp;disallow&nbsp;further&nbsp;input,&nbsp;false&nbsp;to&nbsp;highlight&nbsp;only</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;showFeedback:&nbsp;<span class="js__property">true</span>,&nbsp;<span class="js__sl_comment">//&nbsp;True&nbsp;to&nbsp;always&nbsp;show&nbsp;user&nbsp;feedback,&nbsp;'active'&nbsp;for&nbsp;hover/focus&nbsp;only</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;feedbackTarget:&nbsp;<span class="js__property">null</span>,&nbsp;<span class="js__sl_comment">//&nbsp;jQuery&nbsp;selector&nbsp;or&nbsp;function&nbsp;for&nbsp;element&nbsp;to&nbsp;fill&nbsp;with&nbsp;feedback</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;onFull:&nbsp;null&nbsp;//&nbsp;Callback&nbsp;when&nbsp;full&nbsp;or&nbsp;overflowing,&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__sl_comment">//&nbsp;receives&nbsp;one&nbsp;parameter:&nbsp;true&nbsp;if&nbsp;overflowing,&nbsp;false&nbsp;if&nbsp;not</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;$.extend(<span class="js__operator">this</span>._defaults,&nbsp;<span class="js__operator">this</span>.regional[<span class="js__string">''</span>]);&nbsp;<br><span class="js__brace">}</span>&nbsp;<br>&nbsp;<br>$.extend(MaxLength.prototype,&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__ml_comment">/*&nbsp;Class&nbsp;name&nbsp;added&nbsp;to&nbsp;elements&nbsp;to&nbsp;indicate&nbsp;already&nbsp;configured&nbsp;with&nbsp;max&nbsp;length.&nbsp;*/</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;markerClassName:&nbsp;<span class="js__string">'hasMaxLength'</span>,&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__ml_comment">/*&nbsp;Name&nbsp;of&nbsp;the&nbsp;data&nbsp;property&nbsp;for&nbsp;instance&nbsp;settings.&nbsp;*/</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;propertyName:&nbsp;<span class="js__string">'maxlength'</span>,&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__sl_comment">//&nbsp;Other&nbsp;functions&nbsp;and&nbsp;variables&nbsp;go&nbsp;here...</span>&nbsp;<br><span class="js__brace">}</span>);&nbsp;<br>&nbsp;<br><span class="js__ml_comment">/*&nbsp;Initialise&nbsp;the&nbsp;max&nbsp;length&nbsp;functionality.&nbsp;*/</span>&nbsp;<br><span class="js__statement">var</span>&nbsp;plugin&nbsp;=&nbsp;$.maxlength&nbsp;=&nbsp;<span class="js__operator">new</span>&nbsp;MaxLength();&nbsp;<span class="js__sl_comment">//&nbsp;Singleton&nbsp;instance</span></code></pre>
<p>Our settings include the text to display for the normal feedback (<code>feedbackText</code>),
	the text to display for the overflow feedback (<code>overflowText</code>),
	the maximum length allowed (<code>max</code>),
	whether or not to prevent input after reaching the maximum (<code>truncate</code>),
	whether or not to show the number of characters remaining (<code>showFeedback</code>),
	the identity of an optional control to contain any feedback (<code>feedbackTarget</code>),
	and a callback that is triggered when the textarea fills or overflows (<code>onFull</code>).
	The first two are separated out into a <code>regional</code> array that is indexed
	by language to allow for localisation.</p>
<p>These are the settings that we can see users wanting to change - <em>anticipating
	customisation</em>. Reasonable default values are chosen, although it is likely
	that the maximum number of characters will need to be altered frequently.
	<em>Providing sensible defaults</em> allows the plugin's functionality to be easily
	added to a page and have it work out-of-the-box. Having the
	feedback text as a setting makes it easy for others to change it or translate
	it into another language without affecting the underlying functionalty, while
	using substitution points within it allows us to insert the current values in
	the appropriate spots, regardless of their order or the language used.</p>
<p>The rest of the code is added to the <code>MaxLength.prototype</code>
	allowing it to be accessed from all <code>MaxLength</code> objects.
	We add a function to allow the override of the global defaults,
	which updates the set of default settings and returns.</p>
<pre class="ui-state-active"><code class="js"><span class="js__ml_comment">/*&nbsp;Override&nbsp;the&nbsp;default&nbsp;settings&nbsp;for&nbsp;all&nbsp;max&nbsp;length&nbsp;instances.&nbsp;<br>&nbsp;&nbsp;&nbsp;@param&nbsp;&nbsp;options&nbsp;&nbsp;(object)&nbsp;the&nbsp;new&nbsp;settings&nbsp;to&nbsp;use&nbsp;as&nbsp;defaults&nbsp;<br>&nbsp;&nbsp;&nbsp;@return&nbsp;&nbsp;(MaxLength)&nbsp;this&nbsp;object&nbsp;*/</span>&nbsp;<br>setDefaults:&nbsp;<span class="js__operator">function</span>(options)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;$.extend(<span class="js__operator">this</span>._defaults,&nbsp;options&nbsp;||&nbsp;<span class="js__brace">{</span><span class="js__brace">}</span>);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">return</span>&nbsp;<span class="js__operator">this</span>;&nbsp;<br><span class="js__brace">}</span>,</code></pre>
</article>

<article>
<header><a name="attach"></a>Attachment</header>
<p>When initially attaching the functionality we need to bind events and save the
	settings for the current field instance. We add a marker class to the target
	element to indicate that it has been processed by this plugin. If it has
	already been through the attachment step we ignore it on subsequent calls.
	This approach makes it easy to target multiple, possibly new, occurrences
	on a page without affecting what has gone before.</p>
<pre class="ui-state-active"><code class="js"><span class="js__ml_comment">/*&nbsp;Attach&nbsp;the&nbsp;max&nbsp;length&nbsp;functionality&nbsp;to&nbsp;a&nbsp;textarea.&nbsp;<br>&nbsp;&nbsp;&nbsp;@param&nbsp;&nbsp;target&nbsp;&nbsp;&nbsp;(element)&nbsp;the&nbsp;control&nbsp;to&nbsp;affect&nbsp;<br>&nbsp;&nbsp;&nbsp;@param&nbsp;&nbsp;options&nbsp;&nbsp;(object)&nbsp;the&nbsp;custom&nbsp;options&nbsp;for&nbsp;this&nbsp;instance&nbsp;*/</span>&nbsp;<br>_attachPlugin:&nbsp;<span class="js__operator">function</span>(target,&nbsp;options)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;target&nbsp;=&nbsp;$(target);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(target.hasClass(<span class="js__operator">this</span>.markerClassName))&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">return</span>;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">var</span>&nbsp;inst&nbsp;=&nbsp;<span class="js__brace">{</span>options:&nbsp;$.extend(<span class="js__brace">{</span><span class="js__brace">}</span>,&nbsp;<span class="js__operator">this</span>._defaults),&nbsp;feedbackTarget:&nbsp;$([])<span class="js__brace">}</span>;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;target.addClass(<span class="js__operator">this</span>.markerClassName).&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data(<span class="js__operator">this</span>.propertyName,&nbsp;inst).&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bind(<span class="js__string">'keypress.'</span>&nbsp;+&nbsp;<span class="js__operator">this</span>.propertyName,&nbsp;<span class="js__operator">function</span>(event)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(!inst.options.truncate)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">return</span>&nbsp;<span class="js__property">true</span>;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">var</span>&nbsp;ch&nbsp;=&nbsp;<span class="js__object">String</span>.fromCharCode(&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;event.charCode&nbsp;==&nbsp;<span class="js__property">undefined</span>&nbsp;?&nbsp;event.keyCode&nbsp;:&nbsp;event.charCode);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">return</span>&nbsp;(event.ctrlKey&nbsp;||&nbsp;event.metaKey&nbsp;||&nbsp;ch&nbsp;==&nbsp;<span class="js__string">'\u0000'</span>&nbsp;||&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$(<span class="js__operator">this</span>).val().length&nbsp;&lt;&nbsp;inst.options.max);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>).&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bind(<span class="js__string">'keyup.'</span>&nbsp;+&nbsp;<span class="js__operator">this</span>.propertyName,&nbsp;<span class="js__operator">function</span>()&nbsp;<span class="js__brace">{</span>&nbsp;plugin._checkLength($(<span class="js__operator">this</span>));&nbsp;<span class="js__brace">}</span>);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__operator">this</span>._optionPlugin(target,&nbsp;options);&nbsp;<br><span class="js__brace">}</span>,</code></pre>
<p>When binding the events we are interested in to the target element,
	we use namespaced events (<code>xxx.maxlength</code>, once more using
	the single name claimed from jQuery). This makes it easier to distinguish
	our events from others that may be attached to the same element,
	especially so when it comes to <a href="http://keith-wood.name/pluginFramework.html#destroy">removing them</a> again.</p>
<p>The settings for this instance of the plugin are initialised by
	creating a new empty settings object and extending it with the global defaults.
	These will in turn be overridden by any local settings in the
	<a href="http://keith-wood.name/pluginFramework.html#change"><code>_optionPlugin</code></a> function. The settings are
	linked to the target element via jQuery's <code>$.data</code> mechanism -
	once more using our claimed name (<code>this.propertyName</code>).</p>
</article>
	
<article>
<header><a name="change"></a>Change Settings</header>
<p>A common requirement is to be able to change the settings on a control after
	the plugin functionality has been attached to it. Using the 'option' command
	we allow a group of settings (as an object) or a single named setting and value
	to be updated. If the latter format is used, it is converted into the former
	before processing continues. This function is also reused when
	<a href="http://keith-wood.name/pluginFramework.html#attach">attaching</a> the functionality in the first place
	to initialise the control.</p>
<p>The 'option' command is also used to retrieve one or all setting values for
	a given instance. This situation is identified by the number and type of
	parameters provided to the call and is dealt with first in the code below.</p>
<pre class="ui-state-active"><code class="js"><span class="js__ml_comment">/*&nbsp;Retrieve&nbsp;or&nbsp;reconfigure&nbsp;the&nbsp;settings&nbsp;for&nbsp;a&nbsp;control.&nbsp;<br>&nbsp;&nbsp;&nbsp;@param&nbsp;&nbsp;target&nbsp;&nbsp;&nbsp;(element)&nbsp;the&nbsp;control&nbsp;to&nbsp;affect&nbsp;<br>&nbsp;&nbsp;&nbsp;@param&nbsp;&nbsp;options&nbsp;&nbsp;(object)&nbsp;the&nbsp;new&nbsp;options&nbsp;for&nbsp;this&nbsp;instance&nbsp;or&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(string)&nbsp;an&nbsp;individual&nbsp;property&nbsp;name&nbsp;<br>&nbsp;&nbsp;&nbsp;@param&nbsp;&nbsp;value&nbsp;&nbsp;&nbsp;&nbsp;(any)&nbsp;the&nbsp;individual&nbsp;property&nbsp;value&nbsp;(omit&nbsp;if&nbsp;options&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;is&nbsp;an&nbsp;object&nbsp;or&nbsp;to&nbsp;retrieve&nbsp;the&nbsp;value&nbsp;of&nbsp;a&nbsp;setting)&nbsp;<br>&nbsp;&nbsp;&nbsp;@return&nbsp;&nbsp;(any)&nbsp;if&nbsp;retrieving&nbsp;a&nbsp;value&nbsp;*/</span>&nbsp;<br>_optionPlugin:&nbsp;<span class="js__operator">function</span>(target,&nbsp;options,&nbsp;value)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;target&nbsp;=&nbsp;$(target);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">var</span>&nbsp;inst&nbsp;=&nbsp;target.data(<span class="js__operator">this</span>.propertyName);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(!options&nbsp;||&nbsp;(<span class="js__operator">typeof</span>&nbsp;options&nbsp;==&nbsp;<span class="js__string">'string'</span>&nbsp;&amp;&amp;&nbsp;value&nbsp;==&nbsp;<span class="js__property">null</span>))&nbsp;<span class="js__brace">{</span>&nbsp;<span class="js__sl_comment">//&nbsp;Get&nbsp;option</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">var</span>&nbsp;name&nbsp;=&nbsp;options;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;options&nbsp;=&nbsp;(inst&nbsp;||&nbsp;<span class="js__brace">{</span><span class="js__brace">}</span>).options;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">return</span>&nbsp;(options&nbsp;&amp;&amp;&nbsp;name&nbsp;?&nbsp;options[name]&nbsp;:&nbsp;options);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(!target.hasClass(<span class="js__operator">this</span>.markerClassName))&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">return</span>;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;options&nbsp;=&nbsp;options&nbsp;||&nbsp;<span class="js__brace">{</span><span class="js__brace">}</span>;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(<span class="js__operator">typeof</span>&nbsp;options&nbsp;==&nbsp;<span class="js__string">'string'</span>)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">var</span>&nbsp;name&nbsp;=&nbsp;options;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;options&nbsp;=&nbsp;<span class="js__brace">{</span><span class="js__brace">}</span>;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;options[name]&nbsp;=&nbsp;value;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;$.extend(inst.options,&nbsp;options);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(inst.feedbackTarget.length&nbsp;&gt;&nbsp;<span class="js__num">0</span>)&nbsp;<span class="js__brace">{</span>&nbsp;<span class="js__sl_comment">//&nbsp;Remove&nbsp;old&nbsp;feedback&nbsp;element</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(inst.hadFeedbackTarget)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.feedbackTarget.empty().val(<span class="js__string">''</span>).&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;removeClass(<span class="js__operator">this</span>._feedbackClass&nbsp;+&nbsp;<span class="js__string">'&nbsp;'</span>&nbsp;+&nbsp;<span class="js__operator">this</span>._fullClass&nbsp;+&nbsp;<span class="js__string">'&nbsp;'</span>&nbsp;+&nbsp;<span class="js__operator">this</span>._overflowClass);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">else</span>&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.feedbackTarget.remove();&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.feedbackTarget&nbsp;=&nbsp;$([]);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(inst.options.showFeedback)&nbsp;<span class="js__brace">{</span>&nbsp;<span class="js__sl_comment">//&nbsp;Add&nbsp;new&nbsp;feedback&nbsp;element</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.hadFeedbackTarget&nbsp;=&nbsp;!!inst.options.feedbackTarget;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;($.isFunction(inst.options.feedbackTarget))&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.feedbackTarget&nbsp;=&nbsp;inst.options.feedbackTarget.apply(target[<span class="js__num">0</span>],&nbsp;[]);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">else</span>&nbsp;<span class="js__statement">if</span>&nbsp;(inst.options.feedbackTarget)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.feedbackTarget&nbsp;=&nbsp;$(inst.options.feedbackTarget);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">else</span>&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.feedbackTarget&nbsp;=&nbsp;$(<span class="js__string">''</span>).insertAfter(target);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.feedbackTarget.addClass(<span class="js__operator">this</span>._feedbackClass);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;target.unbind(<span class="js__string">'mouseover.'</span>&nbsp;+&nbsp;<span class="js__operator">this</span>.propertyName&nbsp;+&nbsp;<span class="js__string">'&nbsp;focus.'</span>&nbsp;+&nbsp;<span class="js__operator">this</span>.propertyName&nbsp;+&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__string">'mouseout.'</span>&nbsp;+&nbsp;<span class="js__operator">this</span>.propertyName&nbsp;+&nbsp;<span class="js__string">'&nbsp;blur.'</span>&nbsp;+&nbsp;<span class="js__operator">this</span>.propertyName);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(inst.options.showFeedback&nbsp;==&nbsp;<span class="js__string">'active'</span>)&nbsp;<span class="js__brace">{</span>&nbsp;<span class="js__sl_comment">//&nbsp;Additional&nbsp;event&nbsp;handlers</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;target.bind(<span class="js__string">'mouseover.'</span>&nbsp;+&nbsp;<span class="js__operator">this</span>.propertyName,&nbsp;<span class="js__operator">function</span>()&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.feedbackTarget.css(<span class="js__string">'visibility'</span>,&nbsp;<span class="js__string">'visible'</span>);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>).bind(<span class="js__string">'mouseout.'</span>&nbsp;+&nbsp;<span class="js__operator">this</span>.propertyName,&nbsp;<span class="js__operator">function</span>()&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(!inst.focussed)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.feedbackTarget.css(<span class="js__string">'visibility'</span>,&nbsp;<span class="js__string">'hidden'</span>);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>).bind(<span class="js__string">'focus.'</span>&nbsp;+&nbsp;<span class="js__operator">this</span>.propertyName,&nbsp;<span class="js__operator">function</span>()&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.focussed&nbsp;=&nbsp;<span class="js__property">true</span>;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.feedbackTarget.css(<span class="js__string">'visibility'</span>,&nbsp;<span class="js__string">'visible'</span>);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>).bind(<span class="js__string">'blur.'</span>&nbsp;+&nbsp;<span class="js__operator">this</span>.propertyName,&nbsp;<span class="js__operator">function</span>()&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.focussed&nbsp;=&nbsp;<span class="js__property">false</span>;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.feedbackTarget.css(<span class="js__string">'visibility'</span>,&nbsp;<span class="js__string">'hidden'</span>);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.feedbackTarget.css(<span class="js__string">'visibility'</span>,&nbsp;<span class="js__string">'hidden'</span>);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__operator">this</span>._checkLength(target);&nbsp;<br><span class="js__brace">}</span>,</code></pre>
<p>We retrieve the existing settings for the control (using <code>$.data</code>) and extend
	them with the new settings. Then, based on the new settings, we update the control and
	its associated elements before calling the <a href="http://keith-wood.name/pluginFramework.html#body">body function</a>
	to check the length and thus apply the new settings.</p>
<p>The above code is invoked with the command below, which the <a href="http://keith-wood.name/pluginFramework.html#invoke">
	main function</a> redirects by constructing the name of the method to call.</p>
<pre class="ui-state-active"><code class="js">$(selector).maxlength(<span class="js__string">'option'</span>,&nbsp;<span class="js__brace">{</span>...<span class="js__brace">}</span>);</code></pre>
</article>

<article>
<header><a name="body"></a>Body</header>
<p>Finally we come to the meat of this plugin - the actual checking of the field
	length and updating any feedback. We retrieve the settings for the field (from
	<code>$.data</code>) and truncate the contents if they exceed the specified length.
	Then any feedback field is updated by substituting into the given text
	the current values for the various measures. Of course we are using
	jQuery functionality to perform these changes.</p>
<pre class="ui-state-active"><code class="js"><span class="js__ml_comment">/*&nbsp;Check&nbsp;the&nbsp;length&nbsp;of&nbsp;the&nbsp;text&nbsp;and&nbsp;notify&nbsp;accordingly.&nbsp;<br>&nbsp;&nbsp;&nbsp;@param&nbsp;&nbsp;target&nbsp;&nbsp;(jQuery)&nbsp;the&nbsp;control&nbsp;to&nbsp;check&nbsp;*/</span>&nbsp;<br>_checkLength:&nbsp;<span class="js__operator">function</span>(target)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">var</span>&nbsp;inst&nbsp;=&nbsp;target.data(<span class="js__operator">this</span>.propertyName);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">var</span>&nbsp;value&nbsp;=&nbsp;target.val();&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">var</span>&nbsp;len&nbsp;=&nbsp;value.replace(<span class="js__reg_exp">/\r\n/g</span>,&nbsp;<span class="js__string">'~~'</span>).replace(<span class="js__reg_exp">/\n/g</span>,&nbsp;<span class="js__string">'~~'</span>).length;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;target.toggleClass(<span class="js__operator">this</span>._fullClass,&nbsp;len&nbsp;&gt;=&nbsp;inst.options.max).&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;toggleClass(<span class="js__operator">this</span>._overflowClass,&nbsp;len&nbsp;&gt;&nbsp;inst.options.max);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(len&nbsp;&gt;&nbsp;inst.options.max&nbsp;&amp;&amp;&nbsp;inst.options.truncate)&nbsp;<span class="js__brace">{</span>&nbsp;<span class="js__sl_comment">//&nbsp;Truncation</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">var</span>&nbsp;lines&nbsp;=&nbsp;target.val().split(<span class="js__reg_exp">/\r\n|\n/</span>);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value&nbsp;=&nbsp;<span class="js__string">''</span>;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">var</span>&nbsp;i&nbsp;=&nbsp;<span class="js__num">0</span>;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">while</span>&nbsp;(value.length&nbsp;&lt;&nbsp;inst.options.max&nbsp;&amp;&amp;&nbsp;i&nbsp;&lt;&nbsp;lines.length)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value&nbsp;+=&nbsp;lines[i].substring(<span class="js__num">0</span>,&nbsp;inst.options.max&nbsp;-&nbsp;value.length)&nbsp;+&nbsp;<span class="js__string">'\r\n'</span>;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i++;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;target.val(value.substring(<span class="js__num">0</span>,&nbsp;inst.options.max));&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;target[<span class="js__num">0</span>].scrollTop&nbsp;=&nbsp;target[<span class="js__num">0</span>].scrollHeight;&nbsp;<span class="js__sl_comment">//&nbsp;Scroll&nbsp;to&nbsp;bottom</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;len&nbsp;=&nbsp;inst.options.max;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;inst.feedbackTarget.toggleClass(<span class="js__operator">this</span>._fullClass,&nbsp;len&nbsp;&gt;=&nbsp;inst.options.max).&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;toggleClass(<span class="js__operator">this</span>._overflowClass,&nbsp;len&nbsp;&gt;&nbsp;inst.options.max);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">var</span>&nbsp;feedback&nbsp;=&nbsp;(len&nbsp;&gt;&nbsp;inst.options.max&nbsp;?&nbsp;<span class="js__sl_comment">//&nbsp;Feedback</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.options.overflowText&nbsp;:&nbsp;inst.options.feedbackText).&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;replace(<span class="js__reg_exp">/\{c\}/</span>,&nbsp;len).replace(<span class="js__reg_exp">/\{m\}/</span>,&nbsp;inst.options.max).&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;replace(<span class="js__reg_exp">/\{r\}/</span>,&nbsp;inst.options.max&nbsp;-&nbsp;len).&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;replace(<span class="js__reg_exp">/\{o\}/</span>,&nbsp;len&nbsp;-&nbsp;inst.options.max);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">try</span>&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.feedbackTarget.text(feedback);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">catch</span>(e)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__sl_comment">//&nbsp;Ignore</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">try</span>&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.feedbackTarget.val(feedback);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">catch</span>(e)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__sl_comment">//&nbsp;Ignore</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(len&nbsp;&gt;=&nbsp;inst.options.max&nbsp;&amp;&amp;&nbsp;$.isFunction(inst.options.onFull))&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.options.onFull.apply(target,&nbsp;[len&nbsp;&gt;&nbsp;inst.options.max]);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br><span class="js__brace">}</span>,</code></pre>
<p>This code is invoked in response to <a href="http://keith-wood.name/pluginFramework.html#attach">any keystroke</a> within the field
	or when the settings for the field have <a href="http://keith-wood.name/pluginFramework.html#change">changed</a>.</p>
</article>

<article>
<header><a name="length"></a>Current Length</header>
<p>The '<code>curLength</code>' command differs from the others in that it returns a value
	and doesn't allow chaining of further jQuery functions. In this case it returns an
	object with attributes for the number of characters entered (<code>used</code>) and
	the number of characters remaining (<code>remaining</code>) for the given textarea.</p>
<pre class="ui-state-active"><code class="js"><span class="js__ml_comment">/*&nbsp;Retrieve&nbsp;the&nbsp;counts&nbsp;of&nbsp;characters&nbsp;used&nbsp;and&nbsp;remaining.&nbsp;<br>&nbsp;&nbsp;&nbsp;@param&nbsp;&nbsp;target&nbsp;&nbsp;(jQuery)&nbsp;the&nbsp;control&nbsp;to&nbsp;check&nbsp;<br>&nbsp;&nbsp;&nbsp;@return&nbsp;&nbsp;(object)&nbsp;the&nbsp;current&nbsp;counts&nbsp;with&nbsp;attributes&nbsp;used&nbsp;and&nbsp;remaining&nbsp;*/</span>&nbsp;<br>_curLengthPlugin:&nbsp;<span class="js__operator">function</span>(target)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">var</span>&nbsp;inst&nbsp;=&nbsp;target.data(<span class="js__operator">this</span>.propertyName);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">var</span>&nbsp;value&nbsp;=&nbsp;target.val();&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">var</span>&nbsp;len&nbsp;=&nbsp;value.replace(<span class="js__reg_exp">/\r\n/g</span>,&nbsp;<span class="js__string">'~~'</span>).replace(<span class="js__reg_exp">/\n/g</span>,&nbsp;<span class="js__string">'~~'</span>).length;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">return</span>&nbsp;<span class="js__brace">{</span>used:&nbsp;len,&nbsp;remaining:&nbsp;inst.options.max&nbsp;-&nbsp;len<span class="js__brace">}</span>;&nbsp;<br><span class="js__brace">}</span>,</code></pre>
<p>The above code is invoked with the command below, which the <a href="http://keith-wood.name/pluginFramework.html#invoke">
	main function</a> redirects by constructing the name of the method to call.</p>
<pre class="ui-state-active"><code class="js"><span class="js__statement">var</span>&nbsp;lengths&nbsp;=&nbsp;$(selector).maxlength(<span class="js__string">'curLength'</span>);</code></pre>
</article>

<article>
<header><a name="destroy"></a>Destruction</header>
<p>One of the basic commands that all plugins should implement is the ability to remove
	all the functionality that has been added (events, markup, and settings) and return
	the DOM to the state it was in before the plugin was invoked. Removing any events
	that were added is extremely simple due to the use of a namespace when
	<a href="http://keith-wood.name/pluginFramework.html#attach">adding them</a> - just unbind everything within that namespace.
	This leaves any other events attached to the affected controls intact.</p>
<pre class="ui-state-active"><code class="js"><span class="js__ml_comment">/*&nbsp;Remove&nbsp;the&nbsp;plugin&nbsp;functionality&nbsp;from&nbsp;a&nbsp;control.&nbsp;<br>&nbsp;&nbsp;&nbsp;@param&nbsp;&nbsp;target&nbsp;&nbsp;(element)&nbsp;the&nbsp;control&nbsp;to&nbsp;affect&nbsp;*/</span>&nbsp;<br>_destroyPlugin:&nbsp;<span class="js__operator">function</span>(target)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;target&nbsp;=&nbsp;$(target);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(!target.hasClass(<span class="js__operator">this</span>.markerClassName))&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">return</span>;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">var</span>&nbsp;inst&nbsp;=&nbsp;target.data(<span class="js__operator">this</span>.propertyName);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(inst.feedbackTarget.length&nbsp;&gt;&nbsp;<span class="js__num">0</span>)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">if</span>&nbsp;(inst.hadFeedbackTarget)&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.feedbackTarget.empty().val(<span class="js__string">''</span>).css(<span class="js__string">'visibility'</span>,&nbsp;<span class="js__string">'visible'</span>).&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;removeClass(<span class="js__operator">this</span>._feedbackClass&nbsp;+&nbsp;<span class="js__string">'&nbsp;'</span>&nbsp;+&nbsp;<span class="js__operator">this</span>._fullClass&nbsp;+&nbsp;<span class="js__string">'&nbsp;'</span>&nbsp;+&nbsp;<span class="js__operator">this</span>._overflowClass);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__statement">else</span>&nbsp;<span class="js__brace">{</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.feedbackTarget.remove();&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__brace">}</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;target.removeClass(<span class="js__operator">this</span>.markerClassName&nbsp;+&nbsp;<span class="js__string">'&nbsp;'</span>&nbsp;+&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="js__operator">this</span>._fullClass&nbsp;+&nbsp;<span class="js__string">'&nbsp;'</span>&nbsp;+&nbsp;<span class="js__operator">this</span>._overflowClass).&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;removeData(<span class="js__operator">this</span>.propertyName).&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unbind(<span class="js__string">'.'</span>&nbsp;+&nbsp;<span class="js__operator">this</span>.propertyName);&nbsp;<br><span class="js__brace">}</span></code></pre>
<p>The above code is invoked with the command below, which the <a href="http://keith-wood.name/pluginFramework.html#invoke">
	main function</a> redirects by constructing the name of the method to call.</p>
<pre class="ui-state-active"><code class="js">$(selector).maxlength(<span class="js__string">'destroy'</span>);</code></pre>
</article>

<article>
<header><a name="test"></a>Testing</header>
<p>Obviously testing is very important to ensure that the plugin works the way
	we expect. I've found a combination of informal visual and user interaction
	testing alongside automated functional tests works well.</p>
<p>For debugging plugins I use <a href="http://www.mozilla.com/">Firefox</a> and
	<a href="http://getfirebug.com/">Firebug</a> along with
	<a href="http://jquery.glyphix.com/"><code>jquery.debug.js</code></a>.
	With the latter we can include statements like the following to
	record useful information without disrupting the flow of the processing.
	When used with Firebug these messages appear in the console, while for
	other browsers they are added to a list appearing at the end of the page.</p>
<pre class="ui-state-active"><code class="js">$.log(message);</code></pre>
<p>Once the plugin is working well on Firefox, we still need to check it on the other
	major browsers as there are some differences between their implementations.</p>
<p>For unit testing I use <a href="http://qunitjs.com/">QUnit</a>
	and create a HTML page that contains the JavaScript for the tests.
	Add standard sections to the page to (nicely) show the results of the tests
	and to contain any UI components that we are testing against. The
	<code>qunit-fixture</code> division is positioned off the page to only show the
	test results, while still having the controls accessible and "visible" to our tests.</p>
<pre class="ui-state-active"><code class="html"><span class="html__tag_start">&lt;div</span>&nbsp;<span class="html__attr_name">id</span>=<span class="html__attr_value">"qunit"</span><span class="html__tag_start">&gt;</span><span class="html__tag_end">&lt;/div&gt;</span>&nbsp;<br><span class="html__tag_start">&lt;div</span>&nbsp;<span class="html__attr_name">id</span>=<span class="html__attr_value">"qunit-fixture"</span><span class="html__tag_start">&gt;</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;UI&nbsp;components&nbsp;here...&nbsp;<br><span class="html__tag_end">&lt;/div&gt;</span></code></pre>
<p>Testing can be grouped into modules, which can then be broken down into sections,
	each of which consists of a call to <code>test(name, function)</code>
	with the actual tests appearing in the callback function given here.
	We should notify how many tests are going to be run within each section with
	<code>expect(number)</code>. Then run the code and make assertions with any of the following:</p>
<pre class="ui-state-active"><code class="js">ok(test,&nbsp;description);&nbsp;<span class="js__sl_comment">//&nbsp;Single&nbsp;test&nbsp;</span>&nbsp;<br>equal(v1,&nbsp;v2,&nbsp;description);&nbsp;<span class="js__sl_comment">//&nbsp;Compare&nbsp;two&nbsp;values</span>&nbsp;<br>notEqual(v1,&nbsp;v2,&nbsp;description);&nbsp;<span class="js__sl_comment">//&nbsp;Ensure&nbsp;two&nbsp;different&nbsp;values</span>&nbsp;<br>deepEqual(obj1,&nbsp;obj2,&nbsp;description);&nbsp;<span class="js__sl_comment">//&nbsp;Compare&nbsp;two&nbsp;objects</span>&nbsp;<br>notDeepEqual(obj1,&nbsp;obj2,&nbsp;description);&nbsp;<span class="js__sl_comment">//&nbsp;Ensure&nbsp;two&nbsp;different&nbsp;objects</span></code></pre>
<p>We need to make sure that we cover as many conditions and functions as we can in
	these tests. More is generally better as they are very easy to run.
	We should try to cover edge and error conditions as well as the standard functionality.</p>
<p>We can simulate user events by adding
	<a href="http://code.google.com/p/jqueryjs/source/browse/trunk/plugins/simulate/jquery.simulate.js?spec=svn6063&r=6063">
	<code>jquery.simulate.js</code></a> to the page.
	Use a jQuery selector to locate the target control, then invoke the event by name
	and pass along any additional parameters for that call.</p>
<pre class="ui-state-active"><code class="js">$(selector).simulate(<span class="js__string">'mouseover'</span>,&nbsp;<span class="js__brace">{</span><span class="js__brace">}</span>)&nbsp;<br>$(selector).simulate(<span class="js__string">'keypress'</span>,&nbsp;<span class="js__brace">{</span>charCode:&nbsp;<span class="js__string">'a'</span>.charCodeAt(<span class="js__num">0</span>)<span class="js__brace">}</span>)&nbsp;<br>$(selector).simulate(<span class="js__string">'keydown'</span>,&nbsp;<span class="js__brace">{</span>ctrlKey:&nbsp;<span class="js__property">true</span>,&nbsp;keyCode:&nbsp;$.simulate.VK_HOME<span class="js__brace">}</span>)</code></pre>
<p>If everything passes we'll see a <a href="http://keith-wood.name/maxlength.tests.html">green bar across
	the top of the page</a>. If not, it's a red bar and back to the drawing board.
	Don't forget to test the plugin in all of the major browsers.
	If we don't, we can be sure that someone else will.</p>
</article>

<article>
<header><a name="publish"></a>Publishing</header>
<p>To enable users to be the most out of our plugin, we need to document it and its features.
	Include a list of all of the possible settings for invoking the plugin (plus their expected
	types and default values), along with all of the commands that can be executed upon it and
	their parameters. Make sure you highlight any command(s) that prevent chaining of further
	jQuery functions. See an <a href="http://keith-wood.name/maxlengthRef.html">example</a> for the MaxLength plugin.</p>
<p>We should also prepare a demonstration page to show the plugin to its best advantage.
	Provide examples of the various abilities of the plugin and include the code
	that produces them to allow others to quickly achieve the same effect
	and to learn from our examples. See a <a href="http://keith-wood.name/maxlength.html">sample</a>
	for the MaxLength plugin.</p><p>
</p><p>The other benefit of providing a demonstration page is that we get to see the
	plugin from the user's point of view. Is it easy to use and configure?
	Are its commands and functions consistent? It also provides an additional
	test-bed for the plugin, as not everything can be checked in an automated unit test.
	Once again, check the demonstration across all the major browsers -
	often there are visual aspects that differ between them.</p>
<p>Package all of our JavaScript, CSS, and images into a ZIP file for ease of distribution.
	Include minimised and/or packed versions so that others may reduce their download costs
	without having to go through this process themselves.
	See <a href="http://dean.edwards.name/packer/">Dean Edward's Packer</a> tool,
	which can produce compressed (min) or encoded (pack) versions. Alternately, see the
	<a href="http://code.google.com/closure/compiler/">Google Closure Compiler</a> for similar
	functionality. Include a basic demonstration page to get users started.</p>
<p>And, finally, publish the plugin at the <a href="http://plugins.jquery.com/">
	jQuery plugin repository</a> so that others can benefit from our efforts.</p>
</article>

<article>
<header><a name="download"></a>Downloads</header>
<p>You can download the <a href="http://keith-wood.name/zip/jquery.maxlength.package-1.1.0.zip" onclick="pageTracker._trackPageview(&#39;/downloads/maxlength-1.1.0&#39;);">MaxLength plugin</a>
	as it would be published and the <a href="http://keith-wood.name/zip/jquery.maxlength.support-1.1.0.zip" onclick="pageTracker._trackPageview(&#39;/downloads/maxlength.support-1.1.0&#39;);">
	supporting files</a>, including the unit tests.</p>
</article>
</section>
<footer>
<p>Contact <a href="http://keith-wood.name/index.html">Keith Wood</a> at kbwood{at}iinet.com.au 
	with comments or suggestions.</p>
<hr>
<p>
	© 2010-13, Keith Wood
	<a id="valid" href="http://validator.w3.org/check?uri=http://keith-wood.name/pluginFramework.html">
		<img src="./A jQuery Plugin Framework_files/html5-small.png" alt="Valid HTML 5"></a>
</p>
</footer>
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script><script src="./A jQuery Plugin Framework_files/ga.js" type="text/javascript"></script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4715900-1");
pageTracker._initData();
pageTracker._trackPageview();
</script><div id="goog-gt-tt" class="skiptranslate" dir="ltr"><div style="padding: 8px;"><div><div class="logo"><img src="./A jQuery Plugin Framework_files/translate-32.png" width="20" height="20"></div></div></div><div class="top" style="padding: 8px; float: left; width: 100%;"><h1 class="title gray">Texto original</h1></div><div class="middle" style="padding: 8px;"><div class="original-text"></div></div><div class="bottom" style="padding: 8px;"><div class="activity-links"><span class="activity-link">Sugira uma tradução melhor</span><span class="activity-link"></span></div><div class="started-activity-container"><hr style="color: #CCC; background-color: #CCC; height: 1px; border: none;"><div class="activity-root"></div></div></div><div class="status-message" style="display: none;"></div></div>

</body></html>